package com.app.backend.activiti.serviceImpl;

import cn.hutool.core.collection.CollectionUtil;
import com.app.backend.activiti.dao.ActProcessDao;
import com.app.backend.activiti.entity.ActBiz;
import com.app.backend.activiti.entity.ActProcess;
import com.app.backend.activiti.service.ActNodeService;
import com.app.backend.activiti.service.ActProcessService;
import com.app.backend.activiti.vo.ProcessNodeVo;
import com.app.backend.base.entity.Role;
import com.app.backend.base.entity.User;
import com.app.backend.base.service.UserRoleService;
import com.app.backend.base.utils.MessageUtil;
import com.app.backend.common.base.BaseDao;
import com.app.backend.common.constant.ActConstant;
import com.app.backend.common.exception.AppException;
import com.app.backend.common.utils.RequestContextHandler;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.engine.IdentityService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmActivity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.List;

/**
 * @program: ActProcessServiceImpl
 * @description:
 * @author: Zhun.Xiao
 * @create: 2020-02-27 09:59
 **/
@Service
@Transactional
public class ActProcessServiceImpl implements ActProcessService {
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ActProcessDao actProcessDao;
    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private IdentityService identityService;
    @Autowired
    ActNodeService actNodeService;

    @Autowired
    UserRoleService userRoleService;
    @Autowired
    private MessageUtil messageUtil;
    @Autowired
    private TaskService taskService;


    @Override
    public BaseDao<ActProcess, String> getRepository() {
        return actProcessDao;
    }

    @Override
    public void setAllOldByProcessKey(String key) {
        List<ActProcess> processes = actProcessDao.findByProcessKey(key);
        if (CollectionUtil.isEmpty(processes)) return;
        processes.forEach(process -> process.setLatest(false));
        actProcessDao.saveAll(processes);
    }

    @Override
    public void setLatestByProcessKey(String processKey) {
        ActProcess pr = actProcessDao.findTopByProcessKeyOrderByVersionDesc(processKey);
        if (pr == null) {
            return;
        }
        pr.setLatest(true);
        actProcessDao.save(pr);
    }

    @Override
    public ActProcess findByProcessKeyAndLatest(String key, Boolean latest) {
        return actProcessDao.findByProcessKeyAndLatest(key, latest);
    }

    @Override
    public ProcessNodeVo getFirstNode(String procDefId) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(procDefId);
        ProcessNodeVo node = new ProcessNodeVo();
        List<Process> processes = bpmnModel.getProcesses();
        Collection<FlowElement> flowElements = processes.get(0).getFlowElements();
        StartEvent startEvent = null;
        for (FlowElement flowElement : flowElements) {
            if (flowElement instanceof StartEvent) {
                startEvent = (StartEvent) flowElement;
                break;
            }
        }

        FlowElement flowElement = null;
        SequenceFlow sequenceFlow = startEvent.getOutgoingFlows().get(0);
        for (FlowElement element : flowElements) {
            if (element.getId().equals(sequenceFlow.getTargetRef())) {
                if (element instanceof UserTask) {
                    flowElement = element;
                    node.setType(ActConstant.NODE_TYPE_TASK);
                    break;
                } else if (element instanceof ExclusiveGateway) {
                    flowElement = element;
                    node.setType(ActConstant.NODE_TYPE_EG);
                    break;

                } else if (element instanceof ParallelGateway) {
                    flowElement = element;
                    node.setType(ActConstant.NODE_TYPE_PG);
                    break;
                } else {
                    throw new AppException("开始节点后只能是用户任务节点/排他网关/并行网关");
                }
            }
        }
        if (flowElement instanceof ExclusiveGateway || flowElement instanceof ParallelGateway) {
            return node;
        }
        node.setTitle(flowElement.getName());
        // 设置关联用户
        List<User> users = getNodetUsers(flowElement.getId());
        node.setUsers(users);
        return node;
    }

    @Override
    public ProcessNodeVo getNextNode(String procDefId, String currActId) {

        ProcessNodeVo node = new ProcessNodeVo();
        // 当前执行节点id
        ProcessDefinitionEntity dfe = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(procDefId);
        // 获取所有节点
        List<ActivityImpl> activitiList = dfe.getActivities();
        // 判断出当前流程所处节点，根据路径获得下一个节点实例
        for (ActivityImpl activityImpl : activitiList) {
            if (activityImpl.getId().equals(currActId)) {
                // 获取下一个节点
                List<PvmTransition> pvmTransitions = activityImpl.getOutgoingTransitions();
                PvmActivity pvmActivity = pvmTransitions.get(0).getDestination();
                String type = pvmActivity.getProperty("type").toString();
                if ("userTask".equals(type)) {
                    // 用户任务节点
                    node.setType(ActConstant.NODE_TYPE_TASK);
                    node.setTitle(pvmActivity.getProperty("name").toString());
                    // 设置关联用户
                    List<User> users = getNodetUsers(pvmActivity.getId());
                } else if ("exclusiveGateway".equals(type)) {
                    // 排他网关
                    node.setType(ActConstant.NODE_TYPE_EG);
                } else if ("parallelGateway".equals(type)) {
                    // 平行网关
                    node.setType(ActConstant.NODE_TYPE_PG);
                } else if ("endEvent".equals(type)) {
                    // 结束
                    node.setType(ActConstant.NODE_TYPE_END);
                } else {
                    throw new AppException("流程设计错误，包含无法处理的节点");
                }
                break;
            }
        }
        return node;
    }

    @Override
    public ProcessNodeVo getNode(String nodeId) {
        ProcessNodeVo node = new ProcessNodeVo();
        // 设置关联用户
        List<User> users = getNodetUsers(nodeId);
        node.setUsers(users);
        return node;
    }

    @Override
    public String startProcess(ActBiz actBiz) {

        String userId = RequestContextHandler.getCurrentUserId();
        // 启动流程用户
        identityService.setAuthenticatedUserId(userId);
        // 启动流程 需传入业务表id变量
        actBiz.getParams().put("tableId", actBiz.getTableId());
        ProcessInstance pi = runtimeService.startProcessInstanceById(actBiz.getProcDefId(), actBiz.getId(), actBiz.getParams());
        // 设置流程实例名称
        runtimeService.setProcessInstanceName(pi.getId(), actBiz.getTitle());
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(pi.getId()).list();
        for (Task task : tasks) {
            if (actBiz.getFirstGateway()) {
                // 网关类型
                List<User> users = getNode(task.getTaskDefinitionKey()).getUsers();
                // 如果下个节点未分配审批人为空 取消结束流程
                if (users == null || users.size() == 0) {
                    throw new AppException("任务节点未分配任何候选审批人，发起流程失败");
                } else {
                    // 分配了节点负责人分发给全部
                    for (User user : users) {
                        taskService.addCandidateUser(task.getId(), user.getId());
                        // 异步发消息
                        messageUtil.sendActMessage(user.getId(), ActConstant.MESSAGE_TODO_CONTENT, actBiz.getSendMessage(), actBiz.getSendEmail());
                    }
                }
            } else {
                // 分配第一个任务用户
                for (String assignee : actBiz.getAssignees()) {
                    taskService.addCandidateUser(task.getId(), assignee);
                    // 异步发消息
                    messageUtil.sendActMessage(assignee, ActConstant.MESSAGE_TODO_CONTENT, actBiz.getSendMessage(), actBiz.getSendEmail());
                }
            }
            // 设置任务优先级
            taskService.setPriority(task.getId(), actBiz.getPriority());
        }
        return pi.getId();
    }


    /**
     * 设置节点审批人
     *
     * @param nodeId
     */
    public List<User> getNodetUsers(String nodeId) {
        List<User> users = actNodeService.findUserByNodeId(nodeId);
        // 设置关联角色的用户
        List<Role> roles = actNodeService.findRoleByNodeId(nodeId);
        for (Role r : roles) {
            List<User> userList = userRoleService.findUserByRoleId(r.getId());
            users.addAll(userList);
        }
        //去重
        List<User> users1 = CollectionUtil.distinct(users);
        users1.forEach(u -> u.setPassword(null));
        return users1;
    }
}